home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ For TASM / HEAP.PAK / MEM.ASM < prev    next >
Assembly Source File  |  1996-02-21  |  38KB  |  1,360 lines

  1. page 75,132
  2. %title "Object Oriented Memory Management System - TASM 4.0"
  3. ; Copyright (c) 1993 by Borland International
  4.  
  5. ifndef MDL
  6.     display "Error: This module requires that you provide a memory model"
  7.     display "    definition on the command line. I.E. /dMDL=SMALL."
  8.     display "Also, /m must be given on the command line to enable multiple"
  9.     display "    passes."
  10. MDL equ <SMALL>
  11. endif
  12.  
  13. ModuleVersion EQU "0.33"
  14.  
  15. COMMENT ~
  16.  
  17. This memory management system shows how to use TASM 3.0 objected oriented
  18. extensions to create a heap management system. Some features of this heap
  19. management system are:
  20.     - Extensive use of objects for the heap manager and also individual
  21.       blocks of memory
  22.     - Extensibility to allow changing the structure of heap blocks without
  23.       drastic changes to the heap manager
  24.     - Ability to mix many different types of blocks in the same heap,
  25.       for example, disk heap objects interspersed with regular memory
  26.       blocks.
  27.     - Extensive optimizations
  28.  
  29. The memory management system is implemented by several object classes.
  30. All memory accesses are based on requesting a block of memory whose size
  31. is given in paragraphs. Users of the memory system only need to store
  32. segment offsets of the memory blocks that are returned. All memory blocks
  33. can return the offset, where the user may begin storing information,
  34. whenever needed at a later time.
  35.  
  36. memory_system   is the master heap object. It is responsible for allocating
  37.                 a block of memory to be managed as a heap. This block of
  38.                 memory may be smaller than the maximum amount of available
  39.                 memory, enabling the user of this system to setup multiple
  40.                 heaps if desired.
  41.  
  42. memory_block    is the object that has the methods for the operations of
  43.                 individual blocks in the heap. It may be used for blocks
  44.                 that are used, or unused. Although extensive checks
  45.                 of IsFree are made in routines that are not allowed on used
  46.                 blocks of memory, (for example splitting a block apart to
  47.                 create a smaller block to satisfy an allocation request),
  48.                 there are more specialized blocks objects available to
  49.                 handle used blocks and the endblock at the end of the heap.
  50.                 Consequently, the _DO_ISFREE_ equate may be disabled, and
  51.                 routines within memory_block will rely on the method
  52.                 overrides in the memory_usedblock class to filter out calls
  53.                 that should not happen to memory_blocks. This alone results
  54.                 in a 20% speed increase of the memory manager in the
  55.                 set of testing benchmarks!
  56.  
  57. memory_usedblock is an object that handles the operations for blocks on the
  58.                 heap that are in use. It is a descendant of the memory_block
  59.                 object. It is not strictly necessary that blocks on the heap
  60.                 be retyped as memory_usedblock, (by changing the VMT), but
  61.                 it should allow faster operations since the usedblock
  62.                 automatically traps out routines that it shouldn't be allowed
  63.                 to do, thus eliminating the need to check IsFree.
  64.  
  65. memory_endblock is a specialized object that is placed at the end of the
  66.                 heap. It is a descendant of the memory_usedblock object.
  67.                 It should have no size, and is strictly a placemarker so
  68.                 that all other blocks will have a next pointer other than
  69.                 zero, so they will all be able to use segment arithmetic
  70.                 to calculate their sizes.
  71.  
  72.  
  73.      Object Hierarchy:
  74.  
  75.         MEMORY_SYSTEM
  76.  
  77.  
  78.  
  79.         MEMORY_BLOCK
  80.             :
  81.             :
  82.       MEMORY_USEDBLOCK
  83.             :
  84.             :
  85.        MEMORY_ENDBLOCK
  86.  
  87.  
  88.  
  89.  
  90. For compatibility with future memory_block object descendants, a LockBlock
  91. call should be given for all memory blocks prior to usage, and an UnLockBlock
  92. call should be given whenever the object is free to move about. When a
  93. LockBlock call is given, DS:SI will be set on return to point to an actual
  94. memory address that should be used for operating on the area of memory. The
  95. UnLockBlock call must give this same memory address. UnlockBlock returns
  96. a value in DS:SI that can be used for the next LockBlock call.
  97.  
  98. The ALLOC operation of the memory_system always locks it's block prior to
  99. returning. Therefore it always returns a real memory pointer. When the memory
  100. area has been initialized, it may be unlocked, and the return of the
  101. UnLockBlock call may be stored for future accesses to the memory region.
  102.  
  103. This convention can allow implementing a special kind of memory block that
  104. knows how to swap itself to offline storage when it is not in use.
  105. In practice, this system may be implemented by having objects on the heap
  106. that represent unlocked blocks of memory. When this block recieves a lock
  107. call, it creates a region in memory to recieve the memory contents and then
  108. it loads the actual memory contents from offline storage. A pointer to this
  109. new block is returned. Later, when this new block is freed, the old lock
  110. block is informed and the lock block may swap the memory area to disk.
  111.  
  112. Normal memory_blocks, and memory_usedblocks, do not process the LockBlock or
  113. UnLockBlock calls in any way. So how do blocks that implement swapping come
  114. to be included in the memory system?  A process outside the memory_system,
  115. a caller of memory_system, can implement this. The process calls the
  116. memory_system to give it a block of memory. (This block of memory will
  117. probably be the size of the swapping_block object.) This block of memory
  118. can then be retyped by changing it's VMT. The new VMT will then point to
  119. the method table for a different class of object that implements swapping
  120. of it's memory region. (The outside process might make another call to
  121. the swapping_block object that informs the swapping block how big of an
  122. area it is managing.) As long as the memory_block can respond to normal
  123. requests that the memory_system makes, (it will probably respond with the
  124. methods of the memory_usedblock, while any special messages that it takes
  125. are added to the end of the memory_usedblock VMT), the existance of the
  126. block will not cause any problems. It can transparently handle LockBlock and
  127. UnLockBlock calls by creating and destroying new blocks of memory within
  128. the memory_system that it belongs to, because the outside process would
  129. have passed it a handle to the memory_system, and also any handles or
  130. information that it needs for accessing the offline virtual storage that
  131. it swaps information in and out of.
  132.  
  133. Of course, it is possible that the memory_system itself might be able to
  134. transparently create these new types of memory_blocks. If the memory_system
  135. is passed a pointer to the init method for a new type of block, then
  136. it would automatically deal with the new type of block. The init method
  137. for memory_blocks requires the size of the block to be passed. Currently
  138. this is set to be the same size as the block that the memory_system
  139. selected.
  140.  
  141. ~
  142. jumps
  143. page
  144. locals @@
  145.  
  146. ; Define the following to enable the optimization of the rover pointer
  147. ; to shorten time to find a free heap area.
  148. _USE_ROVER_ =1
  149.  
  150.  
  151. ; Define the following to enable a freed block to be combined with
  152. ; any free block that might be immediately before it.
  153. ; Defining this equate causes a tremendous slow down in the deallocation
  154. ; process, since a scan for previous must be done for every free operation.
  155. ; This scan for previous has to walk the entire memory block chain up to
  156. ; the item that is being freed in order to find the previous block and see
  157. ; if it is free.
  158. ; Logic has been added instead to the alloc routine to combine adjacent
  159. ; free blocks that it finds while it is walking forward through the
  160. ; chain of memory blocks.
  161. ;_COMBINE_PREV_=1
  162.  
  163. ; The PREV pointer for unused blocks is not always accurate, since
  164. ; scanprev is not done during dealloc. Do not define the following.
  165. ;_DO_SETPREV_ = 1
  166.  
  167.  
  168. ; Since used blocks have a different VMT pointer than unused blocks,
  169. ; routines that are not valid for used blocks, (e.g. setprev and setsize),
  170. ; can be trapped out from the default routine in the memory_block object.
  171. ; Then those memory block objects can assume that they were called because
  172. ; the block is free. If this equate is not defined, it is important that
  173. ; all used blocks, (and end blocks), are not left as regular memory_blocks,
  174. ; but are instead retyped as what they are by changing the VMT.
  175. ;__DO_ISFREE_ = 1
  176.  
  177.  
  178. .model small,pascal
  179. .code
  180.  
  181. ; Int 21 DOS calls
  182. DOSINT             =   21H
  183. DOSGETMEMBLOCK     =   48H
  184. DOSRELEASEBLOCK    =   49H
  185. DOSRESIZEBLOCK     =   4AH
  186. DOSTERMINATE       =   4CH
  187.  
  188.  
  189. ; Include the utility macros that ease dealing with
  190. ; the Virtual Method Tables.
  191. include vmtutil.inc
  192.  
  193. IsZero macro reg
  194.        or reg,reg
  195.        endm
  196.  
  197. ; Include the names of routines that handle display of numbers.
  198. include display.inc
  199.  
  200.  
  201. ; Cause the VMT tables to be defined as a part of this module.
  202. _MAKE_MEMVMT_ = 1
  203.    include mem.inc
  204.  
  205.  
  206. PAGE
  207. ;************************************ Routines for MEMORY_BLOCK
  208.  
  209. memory_block_reserved_size proc
  210.          mov  bx,offset @memory_block_used_start
  211.          ret
  212. memory_block_reserved_size endp
  213.  
  214.  
  215.  
  216. ; AX has size of block to allocate into the chain.
  217. memory_block_initget proc pascal previousblock:word,nextblock:word
  218.  
  219.          ret
  220. memory_block_initget endp
  221.  
  222.  
  223.  
  224. memory_block_init proc pascal uses ax,\
  225.                               previousblock:word,nextblock:word,\
  226.                               bsize:word
  227.  
  228.          ; Set the VMT PTR within this block!
  229.          mov  [si.@Mptr_memory_block],offset @TableAddr_memory_block
  230. ;if @CodeSize eq 1
  231. ;         mov  word ptr [si.@Mptr_memory_block+2],seg @TableAddr_memory_block
  232. ;endif
  233.  
  234. ifdef _DO_SETPREV_
  235.          mov  ax,[previousbLock]
  236.          mov  word ptr [si.prev],ax
  237. endif
  238.  
  239.          mov  ax,FREE_BLOCK
  240.          mov  word ptr [si.next],ax
  241.          mov  ax,[nextblock]
  242.          mov  word ptr [si.next2],ax
  243.          mov  ax,[bsize]
  244.          mov  [si.blksize],ax
  245.          xor  ax,ax
  246.  
  247.          ; Zero out the unused high words
  248.          mov  word ptr [si.next+2],ax
  249.          mov  word ptr [si.next2+2],ax
  250. ifdef _DO_SETPREV_
  251.          mov  word ptr [si.prev+2],ax
  252. endif
  253.          ret
  254. memory_block_init endp
  255.  
  256.  
  257.  
  258. memory_block_deinit proc
  259.          ret
  260. memory_block_deinit endp
  261.  
  262.  
  263.  
  264. ; AX has the previous block segment
  265. ; If combine is done, DS points to the previous segment.
  266. memory_block_combine proc
  267. ifdef _DO_ISFREE_
  268.          call [si] method memory_block:IsFree
  269.          jnz  @@done
  270. endif
  271.  
  272.          push cx
  273.          mov  cx,ax   ; Save AX, so we can set it's block to point to the
  274.                       ; next of the DS block.
  275.          call [si] method memory_block:GetNext
  276.          mov  ds,cx
  277.          call [si] method memory_block:SetNext
  278.          ; Also, need to update size of previous block
  279.          ; AX still contains seg of block after one being freed.
  280.          sub  ax,cx
  281.          dec  ax
  282.          call [si] method memory_block:SetSize
  283.          pop  cx
  284.  
  285. @@done:
  286.          ret
  287. memory_block_combine endp
  288.  
  289.  
  290.  
  291. mb_show proc uses ds es dx ax
  292. .data
  293. @@used db "Used$"
  294. @@free db "Free" ;,"$"
  295. @@next2 db " Next2:$"
  296. @@prev db " Prev:$"
  297. @@blksize db " Blksize:$"
  298. .code
  299.          call Show_Bracket
  300.          call [si] method memory_block:IsFree
  301.  
  302.          ; Put our pointer into   ES:SI
  303.          push ds
  304.          pop  es
  305.          mov  ax,@data
  306.          mov  ds,ax
  307.  
  308.          ; Print a USED or FREE string
  309.          mov  ah,DOSPRINTSTRING
  310.          jz   @@block_is_free
  311.  
  312.         ; We should never get here if memory_usedblocks are used.
  313.          mov  dx,offset @@used
  314.          int  DOSINT
  315.          jmp  @@done
  316.  
  317. @@block_is_free:
  318.          mov  dx,offset @@free
  319.  
  320. @@show_msg:
  321.          int  DOSINT
  322.  
  323.         ; mov  ah,DOSPRINTSTRING
  324.         ; mov  dx,offset @@next2
  325.         ; int  DOSINT
  326.          mov  ax,word ptr es:[si.next2]
  327.          call ShowHexWord
  328.  
  329. ifdef _DO_SETPREV_
  330.          mov  ah,DOSPRINTSTRING
  331.          mov  dx,offset @@prev
  332.          int  DOSINT
  333.          mov  ax,word ptr es:[si.prev]
  334.          call ShowHexWord
  335. endif
  336.  
  337.          mov  ah,DOSPRINTSTRING
  338.          mov  dx,offset @@blksize
  339.          int  DOSINT
  340.          mov  ax,word ptr es:[si.blksize]
  341.          call ShowHexWord
  342.  
  343. @@done:
  344.          call Show_Endbracket
  345.          ret
  346. mb_show endp
  347.  
  348.  
  349.  
  350. mb_findprev proc
  351.          local findprevfor:word
  352.          mov  [findprevfor],ax
  353.  
  354. @@check:
  355.          LoadVMTSeg  es,ax
  356.          call [si] method memory_block:GetNext
  357.          cmp  ax,[findprevfor]
  358.          je   @@previousfound
  359.  
  360.          IsZero ax
  361.          je   @@noprevious
  362.  
  363.          ; Load the segment of the next block, and go check it out
  364.          mov  ds,ax
  365.          jmp  @@check
  366.  
  367. @@previousfound:
  368.          mov ax,ds      ; Store the segment of the current block in AX.
  369.          jmp @@done
  370.  
  371. @@noprevious:
  372.          xor ax,ax      ;Zero out AX to indicate no previous.
  373. @@done:
  374.          ret
  375. mb_findprev endp
  376.  
  377.  
  378.  
  379. mb_getnext proc
  380. ifdef _DO_ISFREE_
  381.          mov  ax,word ptr [si.next]
  382.          cmp  ax,FREE_BLOCK
  383.          jne  @@done
  384. endif
  385.          mov  ax,word ptr [si.next2]
  386. @@done:
  387.          ret
  388. mb_getnext endp
  389.  
  390.  
  391.  
  392. mb_isfree proc
  393. ifdef _DO_ISFREE_
  394.          cmp word ptr [si.next],FREE_BLOCK
  395. else
  396.          ; All we need to do is set the Z flag!
  397.          cmp ax,ax
  398. endif
  399.          ret
  400. mb_isfree endp
  401.  
  402.  
  403.  
  404. mb_setprev proc
  405. ifdef _DO_SETPREV_
  406. ifdef _DO_ISFREE_
  407.          LoadVMTSeg es,bx
  408.          call [si] method memory_block:IsFree
  409.          jnz  @@1
  410. endif
  411.          mov  word ptr [si.prev],ax
  412. @@1:
  413. endif
  414.          ret
  415. mb_setprev endp
  416.  
  417.  
  418.  
  419. mb_setsize proc
  420. ifdef _DO_ISFREE_
  421.          LoadVMTSeg es,bx
  422.          call [si] method memory_block:IsFree
  423.          jnz  @@1    ; short @memory_block_setsize_done
  424. endif
  425.          mov  word ptr [si.blksize],ax
  426. ifdef _DO_ISFREE_
  427. @@1:
  428. endif
  429.          ret
  430. mb_setsize endp
  431.  
  432.  
  433.  
  434. mb_setnext proc
  435. ifdef _DO_ISFREE_
  436.          LoadVMTSeg es,bx
  437.          call [si] method memory_block:IsFree
  438.          jnz  short @@notfree
  439. endif
  440.  
  441.          ; If we are here, the block is free.
  442.          mov  word ptr [si.next2],ax
  443. ifdef _DO_ISFREE_
  444.          jmp  short @@done
  445.  
  446. @@notfree:
  447.          mov  word ptr [si.next],ax
  448. @@done:
  449. endif
  450.          ret
  451. mb_setnext endp
  452.  
  453.  
  454.  
  455. mb_scan proc
  456.  
  457. ;         LoadVMTSeg es,bx
  458.  
  459. @@check:
  460.          call [si] method memory_block:IsFree
  461. ;         cmp word ptr [si.next],FREE_BLOCK  ; See if this block is free
  462.          jz  @@found
  463.  
  464.          ; It's not free. So go to the next block.
  465.          call [si] method memory_block:GetNext
  466.          IsZero  ax     ; Check for the last block in the chain (next=0?)
  467.          jz  @@done
  468.          mov ds,ax
  469.          jmp @@check
  470.  
  471. @@found:
  472.          ; Load the size of the current block into AX
  473.          ; Size in paragraphs is    (seg of next block) - (seg of this block)
  474.          mov  ax,ds
  475.          neg  ax
  476.          add  ax,word ptr [si.next2]
  477.          dec  ax       ; Adjust for block bookkeeping information
  478. ;         mov  ax,[si.blksize]   ; This should produce the same
  479.                                     ; value for free blocks.
  480.  
  481. @@done:
  482.          ret
  483. mb_scan endp
  484.  
  485.  
  486. mb_rawblocksize proc
  487. ;         LoadVMTSeg es,bx
  488.          call [si] method memory_block:GetNext
  489.          mov  bx,ds
  490.          sub  ax,bx
  491.          ret
  492. mb_rawblocksize endp
  493.  
  494.  
  495.  
  496. mb_breakblock proc uses ds si
  497.          local desiredsize:word,currentnext:word,newnext:word,currblock:word,\
  498.                newblocksize:word
  499.  
  500.          mov  [desiredsize],ax
  501.          mov  ax,ds
  502.          mov  [currblock],ax
  503.          mov  [newnext],ax     ; We'll use newnext as the address to return in
  504.                                ; AX.
  505.  
  506.          ; This routine is only valid for free memory blocks
  507. ;         LoadVMTSeg es,bx
  508. ifdef _DO_ISFREE_
  509.          call [si] method memory_block:IsFree
  510.          jnz  @@done
  511. endif
  512.  
  513.          call [si] method memory_block:RawBlockSize
  514.          mov  bx,ax
  515.          sub  bx,[desiredsize]
  516.          dec  bx     ; Represent bookkeeping information of current block
  517.          dec  bx     ; For bookkeeping information of the new block
  518.          mov  [newblocksize],bx
  519.          cmp  bx,2   ; If less than four paragraphs left over, don't
  520.                      ; do the breakdown.
  521.          jbe  @@done
  522.  
  523.          ; Get the current next block
  524.          call [si] method memory_block:GetNext
  525.          mov  [currentnext],ax
  526.  
  527.          ; Calculate the segment of the new block
  528.          mov  ax,ds
  529.          add  ax,[desiredsize]
  530.          inc  ax             ; For the size of our bookkeeping info
  531.          mov  [newnext],ax
  532.  
  533.          ; Set our next pointer to new block
  534.          call [si] method memory_block:SetNext
  535.  
  536.          ; Set our size
  537.          mov  ax,[desiredsize]
  538.          call [si] method memory_block:SetSize
  539.  
  540.          ; Set the new block to point to its neighbors
  541.          mov  ds,[newnext]
  542.          call [si] method memory_block:init pascal,[currblock],[currentnext],[newblocksize]
  543.  
  544. ifdef _DO_SETPREV_
  545.          ; Set the old next block to point to the new block created just
  546.          ; before it.
  547.          mov  ds,[currentnext]
  548.          call [si] method memory_block:setprev
  549. endif
  550.  
  551. @@done:
  552.          mov  ax,[newnext]
  553.          ret
  554. mb_breakblock endp
  555.  
  556.  
  557.  
  558. memory_block_markfree proc
  559. ifdef _DO_ISFREE_
  560.          local previous:word
  561.  
  562.          mov  [previous],ax
  563.          call [si] method memory_block:IsFree
  564.          jz   @@block_is_free_skip
  565.  
  566.          ; Get the pointer to the next block
  567.          call [si] method memory_block:GetNext
  568.  
  569.          ; Calculate the size of this block
  570.          push ax
  571.  
  572.          mov  bx,ax
  573.          mov  ax,ds
  574.          sub  bx,ax
  575.          dec  bx      ; For the bookkeeping area
  576.  
  577.          pop  ax
  578.          call [si] method memory_block:init  pascal,[previous],ax,bx
  579.  
  580. @@block_is_free_skip:
  581. endif
  582.          ret
  583. memory_block_markfree endp
  584.  
  585.  
  586.  
  587. memory_block_markused proc
  588. ifdef _DO_ISFREE_
  589.          call [si] method memory_block:IsFree
  590.          jnz  @@done
  591. endif
  592.          mov  ax,word ptr [si.next2]
  593.          mov  word ptr [si.next],ax
  594.          mov  ax,word ptr [si.next2+2]
  595.          mov  word ptr [si.next+2],ax
  596.  
  597.          ; Change the block type to be a used block.
  598.          ; Do this by changing the VMT!
  599.          mov  [si.@Mptr_memory_block],offset @TableAddr_memory_usedblock
  600. ;if @CodeSize eq 1
  601. ;         mov  word ptr [si.@Mptr_memory_block+2],seg @TableAddr_memory_usedblock
  602. ;endif
  603. @@done:
  604.          ret
  605. memory_block_markused endp
  606.  
  607.  
  608.  
  609. memory_block_lock proc
  610.  
  611.          ret
  612. memory_block_lock endp
  613.  
  614.  
  615.  
  616. memory_block_unlock proc
  617.  
  618.          ret
  619. memory_block_unlock endp
  620.  
  621.  
  622. memory_block_allocfail proc
  623.          ret
  624. memory_block_allocfail endp
  625.  
  626.  
  627. page
  628. ;***** Routines for  MEMORY_USEDBLOCK
  629.  
  630.  
  631. memory_usedblock_markfree proc
  632.          local previous:word
  633.  
  634.          mov  [previous],ax
  635. ifdef _DO_ISFREE_
  636.          call [si] method memory_block:IsFree
  637.          jz   @@block_is_free_skip
  638. endif
  639.  
  640.          ; Get the pointer to the next block
  641.          call [si] method memory_block:GetNext
  642.  
  643.          ; Calculate the size of this block
  644.          push ax
  645.  
  646.          mov  bx,ax
  647.          mov  ax,ds
  648.          sub  bx,ax
  649.          dec  bx      ; For the bookkeeping area
  650.  
  651.          pop  ax
  652.          call [si] method memory_block:init  pascal,[previous],ax,bx
  653.  
  654. @@block_is_free_skip:
  655.          ret
  656. memory_usedblock_markfree endp
  657.  
  658.  
  659. memory_usedblock_combine proc
  660.          ret    ;Can't do a combine with a used block. So ignore it!
  661. memory_usedblock_combine endp
  662.  
  663.  
  664.  
  665. memory_usedblock_setnext proc
  666.          mov  word ptr [si.next],ax
  667.          ret
  668. memory_usedblock_setnext endp
  669.  
  670.  
  671.  
  672. memory_usedblock_invalid proc
  673.          ret
  674. memory_usedblock_invalid endp
  675.  
  676.  
  677.  
  678. memory_usedblock_isfree proc uses ax
  679.          mov  ax,1    ; Set the Z flag to zero!
  680.          IsZero ax
  681.          ret
  682. memory_usedblock_isfree endp
  683.  
  684.  
  685.  
  686. memory_usedblock_scan proc
  687.          ; This block is not free, so quickly pass the call
  688.          ; along to the next object in this chain!
  689.          mov   ax,word ptr [si.next]
  690.          mov   ds,ax
  691.          IsZero ax   ; Check for zero, meaning end of the line!
  692.          jz    @@done
  693.  
  694.          ; Use a jump, instead of
  695.          ; call  [si] method memory_usedblock:ScanFree
  696.          ; so that we eliminate overflowing the stack during this routine.
  697.          MOV   BX,[([ SI ]).@Mptr_memory_block]
  698. ;if @CodeSize eq 1
  699. ;         MOV   ES,word ptr [([ SI ]).@Mptr_memory_block+2]
  700. ;endif
  701.          jmp   es:[(@Table_memory_usedblock PTR BX).ScanFree]
  702.  
  703. @@done:
  704.          ret
  705. memory_usedblock_scan endp
  706.  
  707.  
  708.  
  709. memory_usedblock_getnext proc
  710.          mov  ax,word ptr [si.next]
  711.          ret
  712. memory_usedblock_getnext endp
  713.  
  714.  
  715.  
  716. memory_usedblock_init proc
  717.          ; Set the VMT PTR within this block!
  718.          mov  [si.@Mptr_memory_block],offset @TableAddr_memory_usedblock
  719.          ret
  720. memory_usedblock_init endp
  721.  
  722.  
  723.  
  724. memory_usedblock_show proc uses ds es dx ax
  725. .data
  726. @@type db "USED$"
  727. @@next db " Next:$"
  728. .code
  729.          call Show_Bracket
  730.          push ds
  731.          pop  es
  732.          mov  ax,@data
  733.          mov  ds,ax
  734.          mov  dx,offset @@type
  735.          mov  ah,DOSPRINTSTRING
  736.          int  DOSINT
  737.  
  738.          mov  ah,DOSPRINTSTRING
  739.          mov  dx,offset @@next
  740.          int  DOSINT
  741.          mov  ax,word ptr es:[si.next]
  742.          call ShowHexWord
  743.  
  744.          call Show_Endbracket
  745.          ret
  746. memory_usedblock_show endp
  747.  
  748.  
  749.  
  750. memory_usedblock_lock proc
  751.  
  752.          ret
  753. memory_usedblock_lock endp
  754.  
  755.  
  756.  
  757. memory_usedblock_unlock proc
  758.  
  759.          ret
  760. memory_usedblock_unlock endp
  761.  
  762.  
  763.  
  764.  
  765. page
  766. ;***** Routines for   MEMORY_ENDBLOCK
  767.  
  768.  
  769. memory_endblock_ignore proc
  770.          ret
  771. memory_endblock_ignore endp
  772.  
  773.  
  774.  
  775. memory_endblock_getnext proc
  776.          xor ax,ax
  777.          ret
  778. memory_endblock_getnext endp
  779.  
  780.  
  781.  
  782. memory_endblock_init proc
  783.          ; Set the VMT PTR within this block!
  784.          mov  [si.@Mptr_memory_block],offset @TableAddr_memory_endblock
  785. ;if @CodeSize eq 1
  786. ;         mov  word ptr [si.@Mptr_memory_block],seg @TableAddr_memory_endblock
  787. ;endif
  788.          xor  ax,ax
  789.          mov  word ptr [si.next],ax
  790.          mov  word ptr [si.next+2],ax
  791.          ret
  792. memory_endblock_init endp
  793.  
  794.  
  795. memory_endblock_show proc uses ds dx ax
  796. .data
  797. @@end db "END$"
  798. .code
  799.          call Show_Bracket
  800.          mov  ax,@data
  801.          mov  ds,ax
  802.          mov  dx,offset @@end
  803.          mov  ah,DOSPRINTSTRING
  804.          int  DOSINT
  805.  
  806.          call Show_Endbracket
  807.          ret
  808. memory_endblock_show endp
  809.  
  810.  
  811.  
  812.  
  813.  
  814. page
  815. ;***** Routines for   MEMORY_SYSTEM
  816.  
  817.  
  818. memory_system_init proc uses es ds si bx
  819.          local endblockseg:word
  820.  
  821.          ; Fill in the VMT for the memory system
  822.          mov  [si.@Mptr_memory_system],offset @TableAddr_memory_system
  823.  
  824.          ; Get a block of memory.
  825.          mov  bx,ax              ; Get the desired size, unless it is zero,
  826.                                  ; then get the largest possible block.
  827.          or   bx,bx
  828.          jnz  @Memory_system_init_Alloc
  829.  
  830.          dec  bx   ;mov  bx,0FFFFh          ; Go for largest possible
  831. @Memory_system_init_Alloc:
  832.          mov  [si.blocksize],bx
  833.          mov  ah,DOSGETMEMBLOCK
  834.          int  DOSINT
  835.  
  836.          ; Need to handle fail here!  (Carryflag set, and BX with size of
  837.          ;                              largest block available. )
  838.          jnc  AllocSuccess
  839.  
  840.          ; Check the high bit of blocksize to see
  841.          ; if we can get a different size.
  842.          ; Note that if we come here a second time, it is because DOS
  843.          ; said a certain size was available, and then it wouldn't give
  844.          ; it when requested. The high bit wont be set on the second call.
  845.          TESTFLAG [si.blocksize],8000h
  846.          jz   AllocFailed
  847.  
  848.          ; BX has size of largest block available. Go get it if it is NZ.
  849.          or   bx,bx
  850.          jnz  @Memory_system_init_Alloc    ; Jump back and get it if there
  851.                                            ; is some available.
  852. AllocFailed:
  853.          mov  [si.blocksize],0
  854.          jmp  @Memory_system_init_done
  855.  
  856. AllocSuccess:
  857.          ; Success:  Carryflag clear, and AX initial segment of allocated block
  858.  
  859.          mov  [si.root],ax     ; Segment of start of block still in AX!
  860.  
  861.          mov  [si.rover],ax
  862.  
  863.          ; Load the size of the block into BX
  864.          mov  bx,[si.blocksize]
  865.          dec  bx     ; For bookkeeping
  866.          dec  bx     ; For the endblock
  867.  
  868.          ; Calculate in CX the segment of the endblock
  869.          mov  cx,bx
  870.          inc  cx     ; For bookkeeping for first block
  871.          add  cx,ax  ; To get segment
  872.          mov  [endblockseg],cx
  873.          mov  [si.last],cx
  874.  
  875.  
  876.          ; Save the address of the memory system, so we
  877.          ; can call constructor for the memory block.
  878.          push ds
  879.          push si
  880.  
  881.          ; Load the pointer to the block into DS:SI
  882.          mov  ds,ax
  883.          xor  si,si
  884.          xor  ax,ax   ; For pushing zeros for prev & next blocks
  885.  
  886. ;         ; Need to set the VMT for the memory block before calling init!
  887. ;         ; This is only needed if INIT routine is in the VMT!
  888. ;         mov  [si.@Mptr_memory_block],offset @TableAddr_memory_block
  889.  
  890.          LoadVMTSeg ES
  891.          call [si] method memory_block:init pascal, ax,cx,bx
  892.  
  893.          ; Now make an endblock
  894.          mov  ds,[endblockseg]
  895.          call [si] method memory_endblock:init
  896.  
  897.  
  898.          ; Restore pointre to the memory system
  899.          pop  si
  900.          pop  ds
  901.  
  902. @Memory_system_init_done:
  903.          mov  [si.usedspace],0
  904.          mov  ax,[si.blocksize]
  905.          dec  ax    ; Bookkeeping for the first block
  906.          dec  ax    ; Bookkeeping for the end block
  907.          mov  [si.freespace],ax
  908.          dec  [si.freespace]
  909.          ret
  910. memory_system_init endp
  911.  
  912.  
  913.  
  914. memory_system_deinit proc
  915.         ; Return the memory to DOS.
  916.          mov  es,[si.root]
  917.          mov  ah,DOSRELEASEBLOCK
  918.          int  DOSINT
  919.          ret
  920. memory_system_deinit endp
  921.  
  922.  
  923.  
  924. memory_system_resetrover proc
  925.          mov  bx,[si.root]
  926.          mov  [si.rover],root
  927.          ret
  928. memory_system_resetrover endp
  929.  
  930.  
  931.  
  932. .data
  933. mss_initial  db "Memory System Show:",CR,LF
  934.              db "System at:$"
  935. mss_root     db "  Root block at:$"
  936. mss_last     db "  Last block at:$"
  937. mss_rover    db " Rover block at:$"
  938. mss_free     db "      Freespace:$"
  939. mss_used     db "      Usedspace:$"
  940. mss_blocks   db "Blocks at:$"
  941. mss_no_blks  db "No blocks in heap.$"
  942. .code
  943. memory_system_show   proc uses ax dx es ds
  944.          ; Move pointer to the memory system from DS:SI, to ES:SI
  945.          push  ds
  946.          pop   es
  947.          ; Now load pointer to our messages
  948.          mov   dx,@data
  949.          mov   ds,dx
  950.          ; Show the location of the memory system
  951.          mov   dx,offset mss_initial
  952.          mov   ah,DOSPRINTSTRING
  953.          int   DOSINT
  954.          mov   ax,es
  955.          call  ShowHexWord
  956.          mov   dl,':'
  957.          mov   al,DOSPRINTCHAR
  958.          int   DOSINT
  959.          mov   ax,si
  960.          call  ShowHexWord
  961.          call  CRLF
  962.  
  963.          ; Show the internal variables of the memory system
  964.          mov   dx,offset mss_root
  965.          mov   ah,DOSPRINTSTRING
  966.          int   DOSINT
  967.          mov   ax,es:[si.root]
  968.          call  ShowHexWord
  969.          call  CRLF
  970.          mov   dx,offset mss_last
  971.          mov   ah,DOSPRINTSTRING
  972.          int   DOSINT
  973.          mov   ax,es:[si.last]
  974.          call  ShowHexWord
  975.          call  CRLF
  976.          mov   dx,offset mss_rover
  977.          mov   ah,DOSPRINTSTRING
  978.          int   DOSINT
  979.          mov   ax,es:[si.rover]
  980.          call  ShowHexWord
  981.          call  CRLF
  982.          mov   dx,offset mss_free
  983.          mov   ah,DOSPRINTSTRING
  984.          int   DOSINT
  985.          mov   ax,es:[si.freespace]
  986.          call  ShowHexWord
  987.          call  CRLF
  988.          mov   dx,offset mss_used
  989.          mov   ah,DOSPRINTSTRING
  990.          int   DOSINT
  991.          mov   ax,es:[si.usedspace]
  992.          call  ShowHexWord
  993.          call  CRLF
  994.  
  995.          ; Save pointer to the memory system
  996.          push  es
  997.          push  si
  998.          mov   ax,es:[si.root]
  999.          xor   si,si
  1000.  
  1001.          ; Check for empty chain
  1002.          cmp   ax,0
  1003.          jz    mss_no_blocks
  1004.  
  1005.          push  ax
  1006.  
  1007.          ; Now we need to walk the chain
  1008.          mov   dx,offset mss_blocks
  1009.          mov   ah,DOSPRINTSTRING
  1010.          int   DOSINT
  1011.  
  1012.          pop   ax
  1013.  
  1014.  
  1015. mss_show_block:
  1016.          mov   ds,ax
  1017.          call  ShowHexWord
  1018.  
  1019.          LoadVMTSeg ES,AX
  1020.          call  [si] method memory_block:show
  1021.  
  1022.          call  [si] method memory_block:GetNext
  1023.          ; See if it is the last block
  1024.          IsZero ax
  1025.          jz    mss_show_block_done
  1026.          push  ax
  1027.          mov   ah,DOSPRINTCHAR
  1028.          mov   dl,','
  1029.          int   DOSINT
  1030.          pop   ax
  1031.          jmp   mss_show_block
  1032.  
  1033. ; Branch here if this heap system is still empty
  1034. mss_no_blocks:
  1035.          mov   dx,offset mss_no_blks
  1036.          mov   ah,DOSPRINTSTRING
  1037.          int   DOSINT
  1038.  
  1039. mss_show_block_done:
  1040.          pop   si
  1041.          pop   es
  1042.  
  1043.          call  CRLF
  1044.  
  1045.          ret
  1046. memory_system_show   endp
  1047.  
  1048.  
  1049.  
  1050. memory_system_freeall   proc
  1051.          mov  ax,[si.root]
  1052. @@free_another:
  1053.          ; If it is the end block, dont try to free it.
  1054.          cmp  ax,[si.last]
  1055.          jz   @@done
  1056.          push ax
  1057.          call [si] method memory_system:free
  1058.          pop  ax
  1059.          push ds
  1060.          push si
  1061.          mov  ds,ax
  1062.          xor  si,si
  1063.          call [si] method memory_block:GetNext
  1064.          IsZero ax
  1065.          pop  si
  1066.          pop  ds
  1067.          jnz  @@free_another
  1068. @@done:
  1069.          ret
  1070. memory_system_freeall   endp
  1071.  
  1072.  
  1073.  
  1074. memory_system_findprev  proc uses ds si
  1075. ;         local systemseg:word,systemofs:word,findprevfor:word
  1076. ;         mov  [systemseg],ds
  1077. ;         mov  [systemofs],si
  1078. ;         mov  [findprevfor],ax
  1079.  
  1080.          ; Check if this block is the root block
  1081.          cmp  ax,[si.root]
  1082.          jz   @memory_system_findprev_atroot
  1083.  
  1084.          ; Make pointer to first block in memory system
  1085.          mov  ds,[si.root]
  1086.          xor  si,si
  1087.  
  1088.          call [si] method memory_block:MatchNext
  1089.          cmp  ax,0
  1090.          jne  @memory_system_findprev_done
  1091.  
  1092.          ; The block wasn't found!
  1093.  
  1094. @memory_system_findprev_atroot:
  1095.  
  1096. @memory_system_findprev_done:
  1097. ;         mov ds,[systemseg]
  1098. ;         mov si,[systemofs]
  1099.          ret
  1100. memory_system_findprev  endp
  1101.  
  1102.  
  1103.  
  1104. memory_system_blockofs proc uses ds si
  1105.          cmp  ax,0
  1106.          jne  @memory_system_blockofs_getit
  1107.          mov  ax,[si.root]  ; Get the root and use it's offset since
  1108.                                ; no segment was passed in
  1109. @memory_system_blockofs_getit:
  1110.          mov  ds,ax
  1111.          xor  si,si
  1112.          call [si] method memory_block:memstart
  1113.          ret
  1114. memory_system_blockofs endp
  1115.  
  1116.  
  1117.  
  1118. memory_system_alloc proc uses ds si
  1119.          local allocsize:word,\
  1120.                allocblock:word,\
  1121.                largest:word,\      ; The size of the largest block encountered
  1122.                largestseg:word,\
  1123.                newfree:word,\
  1124.                rootscan:word,\
  1125.                memsysaddr:dword
  1126.  
  1127.          mov  [largest],0
  1128.          mov  [rootscan],0
  1129.  
  1130.          mov  word ptr [memsysaddr],si
  1131.          mov  word ptr [memsysaddr+2],ds
  1132.  
  1133.          mov  [allocsize],ax
  1134. ifdef _USE_ROVER_
  1135.          mov  ax,[si.rover]
  1136. else
  1137.          mov  ax,[si.root]
  1138. endif
  1139.          mov  ds,ax
  1140. @@scan:
  1141.          xor  si,si
  1142.          call [si] method memory_block:ScanFree
  1143.          ; Check if there is enough room in this free block
  1144.          cmp  ax,[largest]
  1145.          jbe  @@scan_2
  1146.          mov  [largest],ax
  1147.          mov  [largestseg],ds
  1148. @@scan_2:
  1149.          cmp  ax,[allocsize]
  1150.          ja   @@found
  1151.          mov  ax,ds
  1152.          IsZero ax
  1153.          je   @@no_more_blocks
  1154.          call [si] method memory_block:GetNext
  1155.          IsZero ax
  1156.          je   @@no_more_blocks
  1157.  
  1158.  
  1159.          ; The following block may also be free. Try to combine them.
  1160.          ; Swap DS and AX
  1161.          mov  bx,ds   ; DS has the previous block!
  1162.          mov  ds,ax
  1163.          mov  ax,bx   ; Make sure AX has the previous block
  1164.          call [si] method memory_block:Combine
  1165.  
  1166.          jmp  @@scan
  1167.  
  1168.          ; No more blocks to scan!
  1169. @@no_more_blocks:
  1170. ifdef _USE_ROVER_
  1171.          ; Scan again from the root, because a big enough
  1172.          ; block could be prior to rover.
  1173.          cmp  [rootscan],0
  1174.          jne  @@scan_root
  1175.  
  1176.          inc  [rootscan]
  1177.          lds  si,dword ptr [memsysaddr]
  1178.          mov  ax,[si.root]
  1179.  
  1180.          ; Check to see that rover wasn't already pointing at the root
  1181.          cmp  ax,[si.rover]
  1182.          je   @@scan_root
  1183.  
  1184.          mov  ds,ax
  1185.          jmp  @@scan
  1186.  
  1187. @@scan_root:
  1188. endif
  1189.          ; Set rover to point to this new largest memory block,
  1190.          ; because combines might have invalidated the rover pointer.
  1191.          lds  si,dword ptr [memsysaddr]
  1192.          mov  ax,[largestseg]
  1193.          mov  [si.rover],ax
  1194.          xor  ax,ax
  1195.          mov  bx,[largest]
  1196.          jmp  @@done
  1197.  
  1198. @@found:
  1199.          mov  [allocblock],ds
  1200.          ; See how much is leftover in the block after the memory needed
  1201.          ; for this alloc call is taken out
  1202.          mov  ax,[allocsize]
  1203.          call [si] method memory_block:BreakBlock
  1204. ifdef _USE_ROVER_
  1205.          mov  [newfree],ax
  1206. endif
  1207.          ; Mark the block as used!
  1208.          call [si] method memory_block:MarkUsed
  1209.          call [si] method memory_block:LockBlock
  1210.          call [si] method memory_block:memstart
  1211.          push bx
  1212.  
  1213.          ; If enough area was leftover, the extra area has been
  1214.          ; broken off to make a separate block.
  1215.          lds  si,dword ptr [memsysaddr]
  1216. ifdef _USE_ROVER_
  1217.          ; Update rover
  1218.          mov  ax,[newfree]
  1219.          mov  [si.rover],ax
  1220. endif
  1221.  
  1222.          mov  ax,[allocblock]
  1223.          pop  bx
  1224. ;         call [si] method memory_block:memstart
  1225. @@done:
  1226.          ret
  1227. memory_system_alloc endp
  1228.  
  1229.  
  1230.  
  1231. memory_system_free proc uses ds si bx
  1232.          local prevblock:word,\  ; Address of the block previous to this one
  1233.                freeblock:word,\  ; Address of block that is being freed
  1234.                systemaddr:dword
  1235.  
  1236.          mov  word ptr [systemaddr],si
  1237.          mov  word ptr [systemaddr+2],ds
  1238.          mov  [freeblock],ax
  1239.  
  1240.          cmp  ax,0
  1241.          jz   @@done
  1242.  
  1243.          ; Check the block to make sure that it is not already free
  1244.          ; Note that an invalid block address here will likely hang the
  1245.          ; memory manager, since the VMT will not be valid.
  1246.          mov  ds,ax
  1247.          xor  si,si
  1248. ifdef _DO_ISFREE_
  1249.          call [si] method memory_block:IsFree
  1250.          jz   @@done
  1251. endif
  1252.          call [si] method memory_block:UnLockBlock
  1253.  
  1254.          lds  si,dword ptr [systemaddr]
  1255.          mov  ax,[freeblock]
  1256.          cmp  ax,[si.root]
  1257.          je   @@free_root      ; Special routine to free the root block
  1258.  
  1259. ifdef _COMBINE_PREVIOUS_
  1260.          ; Prior to freeing the block, get the address of
  1261.          ; the previous block. If no previous block is found,
  1262.          ; then this is a bogus FREE request and will be ignored.
  1263.          call [si] method memory_system:findprev
  1264.          IsZero ax   ; Check for zero.
  1265.          jz   @@done
  1266.  
  1267.          ; There is a previous block
  1268.          mov  [prevblock],ax
  1269. endif
  1270.  
  1271.          ; Mark the current block as free.
  1272.          mov  ds,[freeblock]
  1273.          xor  si,si
  1274.          call [si] method memory_block:MarkFree
  1275.  
  1276. ifdef _COMBINE_PREVIOUS_
  1277.          mov  ds,[prevblock]
  1278.          xor  si,si
  1279.          call [si] method memory_block:IsFree
  1280.          mov  ds,[freeblock]
  1281.          jnz  @@try_combine_next
  1282. else
  1283.          jmp  @@try_combine_next
  1284. endif
  1285.  
  1286. @@combine_previous:
  1287.          ; The previous block is free, therefore it can be combined with
  1288.          ; this one. Set the previous block to point to our next block.
  1289.          call [si] method memory_block:GetNext
  1290.          mov  ds,[prevblock]
  1291.          call [si] method memory_block:SetNext
  1292.          ; Also, need to update size of previous block
  1293.          ; AX still contains seg of block after one being freed.
  1294.          mov  bx,ds
  1295.          sub  ax,bx
  1296.          dec  ax
  1297.          call [si] method memory_block:SetSize
  1298.          mov  [freeblock],ds
  1299.          jmp  @@try_combine_next
  1300.  
  1301. @@free_root:
  1302.          ; At this point we only have to worry is if the block following
  1303.          ; a free block so it can be combined with this one.
  1304.          ; AX is the block to free.
  1305.          mov  ds,ax
  1306.          xor  si,si
  1307.          xor  ax,ax     ; Because there is not previous block
  1308.          call [si] method memory_block:MarkFree
  1309.  
  1310. @@try_combine_next:
  1311.          call [si] method memory_block:GetNext
  1312.          mov  ds,ax
  1313.          call [si] method memory_block:IsFree
  1314.          jnz  @@No_more_combine
  1315.          mov  ax,[freeblock]
  1316.          mov  [prevblock],ax
  1317.          jmp  @@combine_previous
  1318.  
  1319. @@No_more_combine:
  1320. ifdef _USE_ROVER_
  1321.         ; Just in case the rover might have pointed to a block that was
  1322.         ; combined with another, set rover to the start of the new
  1323.         ; free block that was created.
  1324.          lds  si,dword ptr [systemaddr]
  1325.          mov  bx,[freeblock]
  1326.          mov  [si.rover],bx
  1327. endif
  1328.  
  1329.  
  1330. @@done:
  1331.          ret
  1332. memory_system_free endp
  1333.  
  1334.  
  1335. .data
  1336. LastSeg dw seg zzlastseg
  1337.  
  1338.  
  1339. .code
  1340. shrink_memory proc
  1341.      ; Have to give up extra memory that we don't need
  1342.      ; so the memory system objects can get blocks of memory.
  1343.      mov  bx,[LastSeg]
  1344.      mov  ax,es
  1345.      sub  bx,ax    ; Size of program as a number of segments is in BX
  1346.      mov  ah,DOSRESIZEBLOCK
  1347.      int  DOSINT
  1348.  
  1349.          ret
  1350. shrink_memory endp
  1351.  
  1352.  
  1353.  
  1354. zzlastseg segment
  1355.  
  1356. zzlastseg ends
  1357.  
  1358. end
  1359.  
  1360.